﻿/*
 * Created by: 
 * Created: Wednesday, February 21, 2007
 */

using System;
using System.Linq;
using System.Collections.Generic;
using System.Web;
using Edge.Business.Domain.Application;
using Edge.Common.ComponentModel.Notification;
using Edge.Common.Services;
using Edge.Common.Utils;
using Shop.Business.Domain.Catalog;
using Shop.Business.Domain.Customers;
using Shop.Business.Domain.Pricing;

namespace Shop.Business.Domain.Orders.Logic
{
    public class OrderController
    {
        private Order order;

        public class OperationContext : IServiceProvider
        {
            public OperationContext()
            {
            }

            public void Error(string msg)
            {
                //TODO
                throw new NotImplementedException();
            }

            public void Notitfy(string msg)
            {
                //TODO
                throw new NotImplementedException();
            }

            #region IServiceProvider Members

            ///<summary>
            ///Gets the service object of the specified type.
            ///</summary>
            ///
            ///<returns>
            ///A service object of type serviceType.-or- null if there is no service object of type serviceType.
            ///</returns>
            ///
            ///<param name="serviceType">An object that specifies the type of service object to get. </param><filterpriority>2</filterpriority>
            public object GetService(Type serviceType)
            {
                throw new NotImplementedException();
            }

            #endregion
        }

        public OrderController(Order order)
        {
            if (order == null) throw new ArgumentException("order");
            this.order = order;
        }

        public void ExecuteAndCommmit(IOperation op)
        {
            ExecuteEndCommit(delegate { Execute(op); });
        }

        public void ExecuteAndCommmit(IEnumerable<IOperation> operations)
        {
            ExecuteEndCommit(delegate { Execute(operations); });
        }

        protected delegate void Executor();

        public void Execute(IEnumerable<IOperation> operations)
        {
            // TODO accumulate list of restrictions

            List<FialedRestriction> restrictionFials = new List<FialedRestriction>();

            foreach (IOperation operation in operations)
            {
                try
                {
                    CheckRestriction(operation);
                    if (restrictionFials.Count > 0)
                        continue;
                    operation.Execute(new OperationContext());
                }
                catch (RestrictionException ex)
                {
                    restrictionFials.AddRange(ex.Fails);
                }
            }

            if (restrictionFials.Count > 0)
                throw new RestrictionException(restrictionFials);
        }

        /// <summary>
        /// may throws RestrictionException
        /// </summary>
        /// <param name="op"></param>
        public void Execute(IOperation op)
        {
            CheckRestriction(op);

            op.Execute(new OperationContext());
        }

        protected void CheckRestriction(IOperation op)
        {
            IRestriction restriction = op.GetRestriction();
            if (restriction != null)
            {
                ControllerRestrictionVisitor visitor = new ControllerRestrictionVisitor();
                visitor.Operation = op;
                restriction.Accept(visitor);
            }
        }

        protected void ExecuteEndCommit(Executor executor)
        {
            if (executor == null)
                throw new ArgumentException("executor");

            //    		using (IUnitOfWork unitOfWork = Locator.GetService<IUnitOfWorkManager>().Create())
            //    		{
            executor();
            Locator.GetService<IOrderManager>().Save(order);

            //    			unitOfWork.Commit();
            //    		}
            // TODO transaction scope
            //ActionsUtils.PerformActionsByName(order, "__UpdateActions");
        }
        
        public T CreateOperation<T>()
            where T : BaseOrderOperation, new()
        {
            T op = new T();
            op.OrderObject = order;
            return op;
        }

        public Order MergeOrders(List<OrderItem> orderItems)
        {
            if (order.OrderStatus != OrderStatus.Draft)
            {
                Locator.GetService<INotificationManager>().Error("Не тот статус");
                return null;
            }
            List<IOperation> ops = new List<IOperation>();

            foreach (OrderItem item in orderItems)
            {
                AddProduct addProduct = this.CreateOperation<AddProduct>();
                addProduct.Product = item.Product;
                addProduct.Quantity = item.Quantity;
                addProduct.AddSize = item.AddSize;
                if (addProduct.IsAllowed())
                {
                    ops.Add(addProduct);
                }
            }

            this.ExecuteAndCommmit(ops);
            Locator.GetService<IOrderManager>().Save(order);
            return order;
        }

        public static bool ExistPreorder(Order draftOrder)
        {
            if (draftOrder!= null && draftOrder.Items != null)
                return draftOrder.Items.Any(p => p.Product.Qty < 1);
            return false;
        }

        public static List<OrderItem> RegetItemsToPreorder(ICollection<OrderItem> orderItems)
        {
            IEnumerable<OrderItem> ret = orderItems.Where(p => p.Product.Qty < 1);
            return CollectionsUtils.Reverse(ret);
        }

        public static List<OrderItem> RegetItemsToOrder(ICollection<OrderItem> orderItems)
        {
            IEnumerable<OrderItem> ret = orderItems.Where(p => p.Product.Qty > 0);
            return CollectionsUtils.Reverse(ret);
        }

        public bool DeleteOrderItems(List<OrderItem> orderItems)
        {
//            if (order.Status != OrderStatus.DraftQuote && order.Status != OrderStatus.Quote)
//            {
//                Locator.GetService<INotificationManager>().Error("Quote is not editable!");
//                return false;
//            }
            List<IOperation> ops = new List<IOperation>();
            foreach (OrderItem item in orderItems)
            {
                {
                    if (item.Order == order)
                    {
                        DeleteItem deleteItem = this.CreateOperation<DeleteItem>(item);
                        ops.Add(deleteItem);
                    }
                }
            }
            this.ExecuteAndCommmit(ops);
            return true;
        }

        public static Order GeneratePreorder(List<OrderItem> orderItems, Customer customer, Order oldOrder)
        {
            if (orderItems.Count == 0)
            {
                Locator.GetService<INotificationManager>().Error("Нет продуктов");
                return null;
            }
            Order newOrder = Locator.GetService<IOrderManager>().GetOrderWithStatus(customer,
                                                                                    OrderStatus.Preorder);
            newOrder.DeliveryDate = DateTime.Now.AddDays(15);
            Locator.GetService<IOrderManager>().Save(newOrder);
            foreach (OrderItem item in orderItems)
            {
                OrderItem itemTo = new OrderItem(item.Product, item.AddSize, item.Quantity);
                itemTo.Order = newOrder;
                itemTo.CreatedBy = item.CreatedBy;
                itemTo.PriceItem = item.Product.PriceItem;
                itemTo.Price = item.Product.Price;
                newOrder.Items.Add(itemTo);
                oldOrder.Items.Remove(item);
            }
            Locator.GetService<IOrderManager>().Save(newOrder);
            Locator.GetService<IOrderManager>().Save(oldOrder);
           if (newOrder.Items.Count == 0)
            {
                Locator.GetService<IOrderManager>().Delete(newOrder);
                Locator.GetService<INotificationManager>().Error("Не могу сохранить продукты");
                return null;
            }
            
            return newOrder;
        }

        public static Order GenerateCurrent(Order oldOrder)
        {
            
//            if (orderItems.Count == 0)
//            {
//                Locator.GetService<INotificationManager>().Error("Нет продуктов");
//                return null;
//            }
//            Order newOrder = new Order();
//            newOrder.CreatedBy = oldOrder.CreatedBy;
//            newOrder.Total = orderItems.Sum(p => p.SellPrice);
//            newOrder.Discount = ((Customer)oldOrder.CreatedBy).Discount;
//            newOrder.DeliveryDate = DateTime.Now.AddDays(2);
//            newOrder.OrderStatus = OrderStatus.Current;
//            newOrder.DeliveryAddress = oldOrder.DeliveryAddress;
//            newOrder.DeliveryType = oldOrder.DeliveryType;
//            newOrder.PaymentType = oldOrder.PaymentType;
//            Locator.GetService<IOrderManager>().Save(newOrder);
            
//            oldOrder.DeliveryAddress = null;
//            oldOrder.DeliveryType = null;
            oldOrder.Discount = ((Customer)oldOrder.CreatedBy).Discount;
            //oldOrder.Total = orderItems.Sum(p => p.SellPrice) - orderItems.Sum(p => p.SellPrice) * oldOrder.Discount/100;
            Locator.GetService<IOrderManager>().Save(oldOrder);
//            foreach (OrderItem item in orderItems)
//            {
//                if (item.Product.Qty > 0)
//                {
////                    item.PriceItem = item.Product.PriceItem;
////                    Locator.GetService<IOrderItemManager>().Update(item);
//                    PriceItem priceItem = item.PriceItem;
//                    priceItem.Qty -= 1;
//                    Locator.GetService<IPriceItemManager>().Save(priceItem);
//                }
//            }

//            foreach (OrderItem item in orderItems)
//            {
//                Locator.GetService<IOrderItemManager>().ChangeOrderofItem(item, newOrder);
//                oldOrder.Items.Remove(item);
//                newOrder.Items.Add(item);
//           }
//            OrderCalculator clculator = new OrderCalculator(newOrder);
//            newOrder.Total = calculator.TotalWithDiscount();
            //Locator.GetService<IOrderManager>().Save(newOrder);
            
            //TODO Add discount
            
//            if (newOrder.Items.Count == 0)
//            {
//                Locator.GetService<IOrderManager>().Delete(newOrder);
//                Locator.GetService<INotificationManager>().Error("Не могу сохранить продукты");
//                return null;
//            }

            return oldOrder;
        }

        public Order StayAtDraft()
        {
            if (order.Items.Count == 0)
            {
                Locator.GetService<INotificationManager>().Error("Нет продуктов");
                return null;
            }
            Order newOrder = new Order();
            newOrder.OrderStatus = OrderStatus.Draft;
            Locator.GetService<IOrderManager>().Save(newOrder);
//            OrderController controller = new OrderController(newOrder);
//            List<IOperation> ops = new List<IOperation>();
            IEnumerable<OrderItem> listItems = CollectionsUtils.Reverse(order.Items);
            listItems = listItems.Where(p => p.Product.Qty < 1);
            foreach (OrderItem item in listItems)
            {
                Locator.GetService<IOrderItemManager>().ChangeOrderofItem(item, newOrder);
                order.Items.Remove(item);
//                AddProduct addProduct = controller.CreateOperation<AddProduct>();
//                addProduct.Product = item.Product;
//                addProduct.Quantity = item.Quantity;
//                addProduct.AddSize = item.AddSize;
//                addProduct.ConvertTo = false;
//                ops.Add(addProduct);
            }
            
            //controller.ExecuteAndCommmit(ops);
//            try
//            {
//                foreach (OrderItem item in listItems)
//                {
//                    order.Items.Remove(item);
//                    Locator.GetService<IOrderItemManager>().Delete(item);
//                }
//                Locator.GetService<IOrderManager>().Save(order);
//            }
//            catch (Exception)
//            {
//            }
            if (newOrder.Items.Count == 0)
            {
                Locator.GetService<IOrderManager>().Delete(newOrder);
                Locator.GetService<INotificationManager>().Error("Не могу сохранить продукты");
                return null;
            }

            return newOrder;
        }

        public static void ChangeSize(OrderItem orderItem, Product product)
        {
            switch (orderItem.Order.OrderStatus)
            {
                case OrderStatus.Current:
                case OrderStatus.Archived:
                    orderItem.Product.PriceItem.Qty += orderItem.Quantity;
                    product.PriceItem.Qty -= orderItem.Quantity;
                    Locator.GetService<IPriceItemManager>().Save(product.PriceItem);
                    Locator.GetService<IPriceItemManager>().Save(orderItem.Product.PriceItem);
                    break;
                default:
                    break;
            }

            orderItem.Product = product;
            Locator.GetService<IOrderItemManager>().Save(orderItem);
        }

        public static void AssignItemToOrderStatus(OrderStatus orderStatus, OrderItem orderItem)
        {
            Order order = Locator.GetService<IOrderManager>().GetOrderWithStatus((Customer)orderItem.CreatedBy, orderStatus);
            Order oldOrder = orderItem.Order;
            //oldOrder.Items.Remove(orderItem);
            Locator.GetService<IOrderItemManager>().ChangeOrderofItem(orderItem, order);
            oldOrder.Items.Remove(orderItem);

        }

        public Order FinaliseDraftQuote()
        {
            if (order.OrderStatus != OrderStatus.Draft)
            {
                Locator.GetService<INotificationManager>().Error("Не тот статус");
                return null;
            }
//            if (order.DeliveryAddress == null)
//            {
//                Locator.GetService<INotificationManager>().Error(LocalizedText.NotChangedDeliveryAddress);
//                return null;
//            }
//            if (StringUtils.IsBlankOrNull(order.DeliverContact))
//            {
//                Locator.GetService<INotificationManager>().Error(LocalizedText.NotChangedDeliveryTo);
//                return null;
//            }
            if (order.Items.Count == 0)
            {
                Locator.GetService<INotificationManager>().Error("Нет продуктов");
                return null;
            }
            Order newOrder = new Order();

            newOrder.OrderStatus = OrderStatus.Current;
            newOrder.CustomerContact = order.CustomerContact;

            Locator.GetService<IOrderManager>().Save(newOrder);

            OrderController controller = new OrderController(newOrder);
            List<IOperation> ops = new List<IOperation>();
            OrderController draftController = new OrderController(order);
            foreach (OrderItem item in order.Items)
            {
                if (item.Product.Qty > 0)
                {
                    AddProduct addProduct = controller.CreateOperation<AddProduct>();
                    addProduct.Product = item.Product;
                    addProduct.Quantity = item.Quantity;
                    addProduct.AddSize = item.AddSize;
                    addProduct.ConvertTo = false;
                    if (addProduct.IsAllowed())
                    {
                        ops.Add(addProduct);
                    }
                    DeleteItem deleteItem = draftController.CreateOperation<DeleteItem>(item);
                    ops.Add(deleteItem);
                }
            }

            controller.ExecuteAndCommmit(ops);

            if (newOrder.Items.Count == 0)
            {
                Locator.GetService<IOrderManager>().Delete(newOrder);
                Locator.GetService<INotificationManager>().Error("Не могу сохранить продукты");
                return null;
            }
//
//            controller.SetDelivery(order.DeliveryType);
//            foreach (OrderItem item in newOrder.Items)
//            {
//                if (item.LegacyID == null || item.LegacyID == 0) item.LegacyID = item.Id;
//            }
//            Locator.GetService<IOrderManager>().Save(newOrder);
//
            //Clear Draft Quote
            order.DeliveryAddress = null;
            order.OrderStatus = OrderStatus.Draft;
            Locator.GetService<IOrderManager>().Save(order);
            Locator.GetService<IOrderManager>().Save(newOrder);
            return newOrder;
        }

        public T CreateOperation<T>(OrderItem orderItem)
            where T : BaseOrderItemOperation, new()
        {
            if (orderItem == null)
                throw new ArgumentException("orderItem");

            T op = new T();
            op.OrderItemObject = orderItem;
            return op;
        }

        public IOperation CreateOperation(Type type, OrderItem orderItem)
        {
            if (orderItem == null)
                throw new ArgumentException("orderItem");

            BaseOrderItemOperation op = Activator.CreateInstance(type) as BaseOrderItemOperation;
            if (op == null)
                throw new ArgumentException("Type " + type + " does not inherit from BaseOrderItemOperation");
            op.OrderItemObject = orderItem;
            return op;
        }
    }
}